import { Injectable } from '@nestjs/common'
import { loginWithCodeDto, LoginDto } from './dto/login.dto'
import { BadRequestException } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { User } from '../../../database/entities/user.entity'
import * as bcrypt from 'bcrypt'
import { VerificationCode } from '../../../modules/internal-modules/mail/mail.interface'
import { validateEmail } from 'src/common/utils'
import { MailService } from 'src/modules/internal-modules/mail/mail.service'
import { AuthService } from 'src/modules/internal-modules/auth/auth.service'

@Injectable()
export class LoginService {
  // 存储发送的验证码
  private verificationCodes: Map<string, VerificationCode> = new Map()
  // 存储已注销的令牌
  private invalidatedTokens: Map<string, number> = new Map()

  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
    private readonly mailService: MailService,
    private readonly authService: AuthService
  ) {
    setInterval(() => this.cleanupInvalidatedTokens(), 3600000 * 24) // 每天清理一次过期的无效令牌
  }

  // 使用密码登录
  async loginWithPassword(loginDto: LoginDto) {
    const user = await this.userRepository.findOne({
      where: { email: loginDto.email }
    })

    if (!user) {
      throw new BadRequestException('用户不存在')
    }

    // 验证密码
    const isPasswordValid = await bcrypt.compare(
      loginDto.password,
      user.password
    )

    if (!isPasswordValid) {
      throw new BadRequestException('邮箱或密码错误')
    }

    return this.generateAuthRespon(user)
  }

  // 验证码登录
  async loginWithCode(loginDto: loginWithCodeDto) {
    const user = await this.userRepository.findOne({
      where: { email: loginDto.email }
    })

    if (!user) {
      throw new BadRequestException('用户不存在，请前往注册')
    }

    const verificationData = this.verificationCodes.get(loginDto.email)
    if (!verificationData) {
      throw new BadRequestException('请先获取验证码')
    }

    if (verificationData.code !== loginDto.code) {
      throw new BadRequestException('验证码不正确')
    }

    if (new Date().valueOf() > verificationData.expiresAt) {
      this.verificationCodes.delete(loginDto.email)
      throw new BadRequestException('验证码已过期，请重新获取')
    }

    this.verificationCodes.delete(loginDto.email)

    return this.generateAuthRespon(user)
  }

  // 发送验证码
  async sendVerificationCode(email: string): Promise<void> {
    validateEmail(email)

    // 检查邮箱是否注册过
    const existingUser = await this.userRepository.findOne({ where: { email } })
    if (!existingUser) {
      throw new BadRequestException('用户不存在，请前往注册')
    }

    const codeInfo = this.mailService.generateVerificationCode()
    this.verificationCodes.set(email, codeInfo)

    // 发送验证码邮件
    await this.mailService.sendVerificationCode(email, codeInfo.code)
  }

  // 生成认证响应
  private generateAuthRespon(user: User) {
    const payload = {
      sub: user.id,
      email: user.email
    }
    // 去敏感信息
    const { id, password, ...userInfo } = user

    const token = this.authService.generateToken(payload)

    return { token, userInfo }
  }

  // 退出登录
  async logout(token: string): Promise<void> {
    try {
      const payload = await this.authService.verifyToken(token)

      payload && this.invalidatedTokens.set(token, payload.exp * 1000)
    } catch (error) {}
  }

  // 检查令牌是否已被注销
  isTokenInvalidated(token: string): boolean {
    if (this.invalidatedTokens.has(token)) return true

    const payload = this.authService.verifyToken(token)
    return !Boolean(payload)
  }

  // 清理已过期的无效令牌
  private cleanupInvalidatedTokens(): void {
    const now = Date.now()
    for (const [token, expiry] of this.invalidatedTokens.entries()) {
      if (expiry < now) {
        this.invalidatedTokens.delete(token)
      }
    }
  }
}
